package com.example.handler;

import cn.hutool.core.util.XmlUtil;
import com.example.channel.BootNettyClientChannel;
import com.example.channel.BootNettyClientChannelCache;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.xml.XmlFrameDecoder;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import java.io.IOException;
import java.net.InetSocketAddress;

/**
 * @ClassName: BootNettyChannelInboundHandlerAdapter
 * @Author : 13736
 * @Date :2023/7/8  15:18
 * @Description: TODO
 * @Version :1.0
 */

@Slf4j
@Component
@ChannelHandler.Sharable
public class BootNettyChannelInboundHandlerAdapter extends ChannelInboundHandlerAdapter {


    /**
     * 从服务端收到新的数据时，这个方法会在收到消息时被调用
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception, IOException {
        if (msg == null) {
            return;
        }
        final ByteBuf buffer = (ByteBuf)msg;
        //prints out String representation of xml doc
        log.info("read : {}" + buffer.toString((CharsetUtil.UTF_8)));
        //用于释放引用计数对象。在使用 Netty 的 ByteBuf 或其他引用计数对象时，需要手动管理其引用计数以防止内存泄漏。
        //

        String channelId = ctx.channel().id().toString();
        log.info("------------------------------------------------------------------------");
        String xmlStr = buffer.toString((CharsetUtil.UTF_8));
        Document doc = XmlUtil.parseXml(xmlStr);
        log.info(XmlUtil.toStr(doc));
        log.info("channelRead:read msg:{}", msg.toString());
        log.info("-----------------------------------------------------------------------");
        BootNettyClientChannel bootNettyClientChannel = BootNettyClientChannelCache.get(channelId);
        if (bootNettyClientChannel != null) {
            log.info("to do");
            bootNettyClientChannel.setLast_data(msg.toString());
        }
        //当你对一个引用计数对象完成了自己的操作后，需要通过调用 ReferenceCountUtil.release() 方法来减少该对象的引用计数。如果引用计数减少到零，意味着该对象不再被引用，可以安全地释放或回收该对象的资源。
        ReferenceCountUtil.release(msg);
        //回应服务端
        //ctx.write("I got server message thanks server!");
    }

    /**
     * 从服务端收到新的数据、读取完成时调用
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws IOException {
        log.info("channelReadComplete");
        ctx.flush();
    }

    /**
     * 当出现 Throwable 对象才会被调用，即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("error message {}",cause.getMessage(),cause);
        super.exceptionCaught(ctx, cause);
        String channelId = ctx.channel().id().toString();
        BootNettyClientChannelCache.remove(channelId);
        ctx.close();//抛出异常，断开与客户端的连接
    }

    /**
     * 客户端与服务端第一次建立连接时 执行
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception, IOException {
        super.channelActive(ctx);
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();

        String clientIp = insocket.getAddress().getHostAddress();
        int clientPort = insocket.getPort();
        //获取连接通道唯一标识
        //如果map中不包含此连接，就保存连接
        String channelId = ctx.channel().id().toString();
        // 这里我将通道id作为code来使用，实际是需要msg里来摘取的客户端数据里的唯一值的
        // 如果没有则创建 如果有，更新data值
        BootNettyClientChannel b = BootNettyClientChannelCache.get(channelId);
        if (b == null) {
            BootNettyClientChannel bootNettyChannel = new BootNettyClientChannel();
            bootNettyChannel.setChannel(ctx.channel());
            bootNettyChannel.setCode("server:" + channelId);
            BootNettyClientChannelCache.save(channelId, bootNettyChannel);
            log.info("Netty-Server------客户端【" + channelId + "】连接netty服务器[IP:" + clientIp + "--->PORT:" + clientPort + "]");
        }
    }

    /**
     * 客户端与服务端 断连时 执行
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception, IOException {
        super.channelInactive(ctx);
        String channelId = ctx.channel().id().toString();
        BootNettyClientChannelCache.remove(channelId);
        ctx.close();
    }

}
